package com.gitee.wsl.mathematics.geometry.d2.contour.ext

import com.gitee.wsl.mathematics.geometry.api.ShapeTopology
import com.gitee.wsl.mathematics.geometry.d2.contour.ContourShape
import com.gitee.wsl.mathematics.geometry.d2.contour.ShapeContour
import com.gitee.wsl.mathematics.geometry.d2.contour.region.toRegion2
import com.gitee.wsl.mathematics.geometry.d2.contour.region.toShape
import com.gitee.wsl.mathematics.geometry.d2.contour.region.toShapes
import kotlin.jvm.JvmName

/** Applies a boolean org.openrndr.shape.union operation between the [ShapeContour] and a [ContourShape]. */
@Suppress("unused")
fun ShapeContour.union(other: ContourShape): ContourShape = union(this.shape, other)


/**
 * Applies a boolean org.openrndr.shape.union operation between two [ShapeContour]s.
 */
fun union(from: ShapeContour, add: ShapeContour): ContourShape {
    if (from === ShapeContour.EMPTY && add == ShapeContour.EMPTY) {
        return ContourShape.EMPTY
    }
    if (from === ShapeContour.EMPTY) {
        return add.shape
    }
    if (add === ShapeContour.EMPTY) {
        return from.shape
    }

    return if (from.closed) {
        val result = from.ring2.region().union(add.ring2.region())
        result.toShape()
    } else {
        from.shape
    }
}

/**
 * Applies a boolean org.openrndr.shape.union operation between a [ContourShape] and a [ShapeContour].
 */
fun union(from: ContourShape, add: ShapeContour): ContourShape {
    if (from === ContourShape.EMPTY && add === ShapeContour.EMPTY) {
        return ContourShape.EMPTY
    }
    if (from === ContourShape.EMPTY) {
        return add.shape
    }
    if (add === ShapeContour.EMPTY) {
        return from
    }
    val result = from.region2.union(add.ring2.region())
    return result.toShape()
}

/**
 * Applies a boolean org.openrndr.shape.union operation between two [ContourShape]s.
 */
fun union(from: ContourShape, add: ContourShape): ContourShape {
    return if (from.topology == ShapeTopology.CLOSED) {
        val result = from.region2.union(add.region2)
        result.toShape()
    } else {
        from
    }
}

/**
 * Applies a boolean org.openrndr.shape.union operation between a [List] of [ContourShape]s and a [ShapeContour].
 */
fun union(from: List<ContourShape>, add: ShapeContour): List<ContourShape> {
    return from.toRegion2().union(add.ring2.region()).toShapes()
}

/**
 * Applies a boolean org.openrndr.shape.union operation between a [List] of [ContourShape]s and a [ContourShape].
 */
fun union(from: List<ContourShape>, add: ContourShape): List<ContourShape> {
    return from.toRegion2().union(add.region2).toShapes()
}

/**
 * Applies a boolean org.openrndr.shape.union operation between two [List]s of [ContourShape]s.
 */
fun union(from: List<ContourShape>, add: List<ContourShape>): List<ContourShape> {
    return from.toRegion2().union(add.toRegion2()).toShapes()
}

/**
 * Applies a boolean org.openrndr.shape.union operation iteratively between a [List] of [ContourShape]s and a two-dimensional [List] of [ContourShape]s.
 *
 * [add] is traversed and a boolean [union] is applied between [from] and each element.
 */
@JvmName("unionIterative")
fun union(from: List<ContourShape>, add: List<List<ContourShape>>): List<ContourShape> {
    var left = from
    for (addShapes in add) {
        left = union(left, addShapes)
    }
    return left
}